﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.Xsl;

namespace ReleaseNotesConverter
{
	public partial class _Default : Page
	{
		private const string MAIN_DIRECTORY = @"~\";
		private const string XMLNS_PATTERN = "(xmlns=\")(.)+(\")";
		private const string XMLNS_REPLACEMENT = "";
		private const string XML_FILE_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
		private const string XML_EXTENSION = ".xml";
		private const string TAG_PATTERN = @"(?<tag>(<.+?/?>))";

		private const string FILE_NAME_RELEASE_NOTES = "Release-Notes.xml";
		private const string FILE_NAME_LATEST_INTERNAL_BUILD = "Latest-Internal-build.html";
		private const string FILE_PATH_TEAM_PULSE_FILE_WITH_REMOVED_XMLNS = "TeamPulse-File-Removed-Xmlns.xml";
		private const string FILE_PATH_SIMPLIFIED_TEAM_PULSE_FILE = "Simplified-TeamPulse-File.xml";
		private const string FILE_PATH_TEMPLATE_RELEASE_NOTES = "Template-Release-Notes.xsl";
		private const string FILE_PATH_TEMPLATE_LATEST_INTERNAL_BUILD = "Template-Latest-Internal-Build.xsl";

		private const string MESSAGE_FILE_DOESNT_EXIST = "The file you uploaded does NOT exist!";
		private const string MESSAGE_FILE_CANNOT_BE_LOADED = "The file you specified cannot be loaded!";
		private const string MESSAGE_XMLNS_CANNOT_BE_REMOVED = "There was a problem with removing the xmlns tag from the input file!";
		private const string MESSAGE_INCORRECT_FILE_STRUCTURE = 
			"The specified file doesn't match the default excelXML structure from TeamPulse! " + 
			"Check for incorrect opened or closed tags in bug's or story's text or empty paragraphs in them. " +
			"Please contact Stamo Gochev from ASP.NET TEAM 2";

		protected void Page_Load(object sender, EventArgs e)
		{
			if (Page.IsPostBack)
			{
				this.LabelFileName.Text = "";
			}
		}

		protected void ButtonConvertXmlToLatestInternalBuild_Click(object sender, EventArgs e)
		{
			this.LabelFileName.Text = "";
			ConvertToHtml(FILE_PATH_TEMPLATE_LATEST_INTERNAL_BUILD, FILE_NAME_LATEST_INTERNAL_BUILD);
		}

		protected void ButtonConvertXmlToReleaseNotes_Click(object sender, EventArgs e)
		{
			this.LabelFileName.Text = "";
			ConvertToHtml(FILE_PATH_TEMPLATE_RELEASE_NOTES, FILE_NAME_RELEASE_NOTES);
		}

		private void ConvertToHtml(string templateFileName, string parsedFileName)
		{
			if (FileUpload.HasFile)
			{
				string fileExtension =
				   System.IO.Path.GetExtension(FileUpload.FileName);

				if (fileExtension == XML_EXTENSION)
				{
					try
					{
						string filePathUploadedTeamPulseFile = Server.MapPath(MAIN_DIRECTORY + FileUpload.FileName);
						string filePathRemovedXmlns = Server.MapPath(MAIN_DIRECTORY + FILE_PATH_TEAM_PULSE_FILE_WITH_REMOVED_XMLNS);
						string filePathSimplifiedXmlFile = Server.MapPath(MAIN_DIRECTORY + FILE_PATH_SIMPLIFIED_TEAM_PULSE_FILE);
						string filePathParsedHtml = Server.MapPath(MAIN_DIRECTORY + parsedFileName);
						string filePathParsingTemplate = Server.MapPath(MAIN_DIRECTORY + templateFileName);

						FileUpload.SaveAs(filePathUploadedTeamPulseFile);
						LabelFileName.Text = "XML file successfully parsed!" + "<br>" + "File name: " +
							FileUpload.PostedFile.FileName + "<br>" +
							FileUpload.PostedFile.ContentLength + " kb<br>" + "Content type: " +
							FileUpload.PostedFile.ContentType;

						ParseXmlToHtml(filePathUploadedTeamPulseFile, filePathRemovedXmlns,
							filePathSimplifiedXmlFile, filePathParsedHtml, filePathParsingTemplate);

						CreateHtmlFileForDownloading(filePathRemovedXmlns, parsedFileName);
					}
					catch (Exception ex)
					{
						LabelFileName.Text = "ERROR: " + ex.Message.ToString();
					}
				}
				else
				{
					LabelFileName.Text = "Only .xml files are allowed to be parsed!";
				}
			}
			else
			{
				LabelFileName.Text = "You have not specified a file!";
			}
		}

		private void CreateHtmlFileForDownloading(string filePathRemovedXmlns, string outputFile)
		{
			StreamWriter writer = new StreamWriter(filePathRemovedXmlns);
			FileInfo resultFile = new FileInfo(Server.MapPath(MAIN_DIRECTORY + outputFile));
			Response.Clear();
			Response.AddHeader("content-disposition", "attachment;filename=" + outputFile);
			Response.ContentType = "text/html";
			writer.Close();
			Response.TransmitFile(Server.MapPath(MAIN_DIRECTORY + outputFile), 0, resultFile.Length);
			Response.Flush();
			Response.End();
		}

		private static void ParseXmlToHtml(string filePathTeamPulseXmlFile, string filePathRemovedXmlns, 
			string filePathSimplifiedXmlFile, string filePathParsedHtml, string filePathParsingTemplate)
		{
			CreateFileWithRemovedXmlns(filePathTeamPulseXmlFile, filePathRemovedXmlns);			
			CreateSimplifiedTeamPulseFile(filePathRemovedXmlns, filePathSimplifiedXmlFile);

			XslCompiledTransform xsltParser = new XslCompiledTransform();
			xsltParser.Load(filePathParsingTemplate);
			xsltParser.Transform(filePathSimplifiedXmlFile, filePathParsedHtml);
			
		}

		public static void CreateFileWithRemovedXmlns(string filePathOriginalXmlFile, string filePathRemovedXmlnsFile)
		{
			if (!File.Exists(filePathOriginalXmlFile))
			{
				throw new FileNotFoundException(MESSAGE_FILE_DOESNT_EXIST);
			}

			string textToFind = "xmlns=";

			using (StreamWriter writer = new StreamWriter(filePathRemovedXmlnsFile))
			{
				using (StreamReader reader = File.OpenText(filePathOriginalXmlFile))
				{
					bool isTextFound = false;
					int lineNumber = 0;
					string line = null;
					while ((line = reader.ReadLine()) != null)
					{
						lineNumber++;
						if (line.IndexOf(textToFind) > -1)
						{
							isTextFound = true;
							string replacedLine = Regex.Replace(line, XMLNS_PATTERN, XMLNS_REPLACEMENT);
							writer.WriteLine(replacedLine);
						}
						else
						{
							writer.WriteLine(line);
						}
					}
					if (!isTextFound)
					{
						//Console.WriteLine("The text 'xmlns' wasn't found in the .xml file!");
					}
				}
			}
		}

		private static void CreateSimplifiedTeamPulseFile(string filePathOriginalXmlFile, string filePathSimplifiedXmlFile)
		{
			if (!File.Exists(filePathOriginalXmlFile))
			{
				throw new FileNotFoundException(MESSAGE_FILE_DOESNT_EXIST);
			}

			XmlDocument xmlDocument = new XmlDocument();
			try
			{
				xmlDocument.Load(filePathOriginalXmlFile);
			}
			catch (Exception e)
			{
				Console.WriteLine(MESSAGE_FILE_CANNOT_BE_LOADED + Environment.NewLine + e.Message);
			}

			string xPathQueryMatchRows = "/Workbook/Worksheet/Table/Row";
			string xPathQueryMatchCells = "Cell";
			XmlNodeList rows = xmlDocument.SelectNodes(xPathQueryMatchRows);
			if (rows.Count == 0)
			{
				throw new Exception(MESSAGE_INCORRECT_FILE_STRUCTURE);
			}

			string previousControlName = null;
			using (StreamWriter writer = new StreamWriter(filePathSimplifiedXmlFile))
			{
				writer.WriteLine(XML_FILE_HEADER);
				writer.WriteLine("<controls>");
				writer.WriteLine("<control>");

				List<string> stories = new List<string>();
				List<string> bugs = new List<string>();

				foreach (XmlNode item in rows)
				{
					XmlNodeList cells = item.SelectNodes(xPathQueryMatchCells);

					string currentElementType = cells[0].InnerText;
					string currentElementText = cells[1].InnerText;
					string currentControlName = cells[2].InnerText;

					string controlNameWithRemovedBackSlashes = GetStrippedName(currentControlName);

					if (previousControlName != controlNameWithRemovedBackSlashes)
					{
						WriteStoriesAndBugs(writer, stories, bugs);
						previousControlName = controlNameWithRemovedBackSlashes;
						writer.WriteLine("<control>");
						string controlNameWithoutRadPrefix = controlNameWithRemovedBackSlashes.Replace("Rad", "");
						writer.WriteLine("<area>" + controlNameWithoutRadPrefix + "</area>");

					}
					AddStoryOrBug(stories, bugs, currentElementType, currentElementText);
				}

				WriteStoriesAndBugs(writer, stories, bugs);
				writer.WriteLine("</controls>");
			}

		}


		private static void AddStoryOrBug(List<string> stories, List<string> bugs, string currentElementType, string currentElementText)
		{
			if (currentElementType == "Bug")
			{
				bugs.Add(currentElementText);
			}
			else
			{
				if (currentElementType == "Story")
				{
					stories.Add(currentElementText);
				}
			}
		}

		private static void WriteStoriesAndBugs(StreamWriter writer, List<string> stories, List<string> bugs)
		{
			if (stories.Count > 0)
			{
				writer.WriteLine("<stories>");
				for (int i = 0; i < stories.Count; i++)
				{
					string finalStoryText = HttpUtility.HtmlEncode(stories[i]);
					writer.WriteLine("<story>" + finalStoryText + "</story>");
				}
				writer.WriteLine("</stories>");
			}
			stories.Clear();

			if (bugs.Count > 0)
			{
				writer.WriteLine("<bugs>");
				for (int i = 0; i < bugs.Count; i++)
				{
					string finalBugText = HttpUtility.HtmlEncode(bugs[i]);
					writer.WriteLine("<bug>" + finalBugText + "</bug>");
				}
				writer.WriteLine("</bugs>");
			}
			bugs.Clear();

			writer.WriteLine("</control>");
		}

		private static string GetStrippedName(string controlName)
		{
			int firstBackSlashPosition = controlName.IndexOf("/");
			bool isStripped = false;
			string strippedName = null;
			if (firstBackSlashPosition > 0)
			{
				int secondBackSlashPosition = controlName.IndexOf("/", firstBackSlashPosition + 1);
				if (secondBackSlashPosition > 0)
				{
					strippedName = controlName.Substring(firstBackSlashPosition + 1, secondBackSlashPosition - 5).Trim();
				}
				else
				{
					strippedName = controlName.Substring(firstBackSlashPosition + 1).Trim();
				}
				isStripped = true;
			}

			if (isStripped == true)
			{
				return strippedName;
			}

			return controlName;
		}

		private static string GetTextWithoutTagBrackets(string text)
		{
			string copiedText = text;

			Match match = Regex.Match(text, TAG_PATTERN);
			while (match.Success)
			{
				string tagText = match.Groups["tag"].Value;
				string innerText = tagText.Substring(1, tagText.Length - 2);
				copiedText = copiedText.Replace(tagText, innerText);
				match = match.NextMatch();
			}
			return copiedText;
		}

		protected void ButtonConvertCSVToReleaseNotes_Click(object sender, EventArgs e)
		{
			this.LabelFileName.Text = "stamo";
			this.LabelFileName.Visible = true;
		}
	}
}